// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <utility>

#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "media/base/fake_demuxer_stream.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/base/timestamp_constants.h"
#include "media/filters/decoder_stream.h"
#include "media/filters/fake_video_decoder.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Assign;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;

static const int kNumConfigs = 4;
static const int kNumBuffersInOneConfig = 5;

namespace media {

struct VideoFrameStreamTestParams {
    VideoFrameStreamTestParams(bool is_encrypted,
        bool has_decryptor,
        int decoding_delay,
        int parallel_decoding)
        : is_encrypted(is_encrypted)
        , has_decryptor(has_decryptor)
        , decoding_delay(decoding_delay)
        , parallel_decoding(parallel_decoding)
    {
    }

    bool is_encrypted;
    bool has_decryptor;
    int decoding_delay;
    int parallel_decoding;
};

class VideoFrameStreamTest
    : public testing::Test,
      public testing::WithParamInterface<VideoFrameStreamTestParams> {
public:
    VideoFrameStreamTest()
        : demuxer_stream_(new FakeDemuxerStream(kNumConfigs,
            kNumBuffersInOneConfig,
            GetParam().is_encrypted))
        , cdm_context_(new StrictMock<MockCdmContext>())
        , is_initialized_(false)
        , num_decoded_frames_(0)
        , pending_initialize_(false)
        , pending_read_(false)
        , pending_reset_(false)
        , pending_stop_(false)
        , num_decoded_bytes_unreported_(0)
        , has_no_key_(false)
    {
        int decoding_delay = GetParam().decoding_delay;
        int parallel_decoding = GetParam().parallel_decoding;
        BytesDecodedCB bytes_decoded_cb = base::Bind(
            &VideoFrameStreamTest::OnBytesDecoded, base::Unretained(this));

        decoder1_ = new FakeVideoDecoder(decoding_delay, parallel_decoding,
            bytes_decoded_cb);
        decoder2_ = new FakeVideoDecoder(decoding_delay, parallel_decoding,
            bytes_decoded_cb);
        decoder3_ = new FakeVideoDecoder(decoding_delay, parallel_decoding,
            bytes_decoded_cb);

        // TODO(xhwang): We should test the case where only certain decoder
        // supports encrypted streams. Currently this is hard to test becasue we use
        // parameterized tests which need to pass in all combinations.
        if (GetParam().is_encrypted && !GetParam().has_decryptor) {
            decoder1_->EnableEncryptedConfigSupport();
            decoder2_->EnableEncryptedConfigSupport();
            decoder3_->EnableEncryptedConfigSupport();
        }

        ScopedVector<VideoDecoder> decoders;
        decoders.push_back(decoder1_);
        decoders.push_back(decoder2_);
        decoders.push_back(decoder3_);

        video_frame_stream_.reset(new VideoFrameStream(
            message_loop_.task_runner(), std::move(decoders), new MediaLog()));

        if (GetParam().has_decryptor) {
            decryptor_.reset(new NiceMock<MockDecryptor>());

            // Decryptor can only decrypt (not decrypt-and-decode) so that
            // DecryptingDemuxerStream will be used.
            EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
                .WillRepeatedly(RunCallback<1>(false));
            EXPECT_CALL(*decryptor_, Decrypt(_, _, _))
                .WillRepeatedly(Invoke(this, &VideoFrameStreamTest::Decrypt));
        }

        EXPECT_CALL(*cdm_context_, GetDecryptor())
            .WillRepeatedly(Return(decryptor_.get()));
    }

    ~VideoFrameStreamTest()
    {
        // Check that the pipeline statistics callback was fired correctly.
        EXPECT_EQ(num_decoded_bytes_unreported_, 0);

        is_initialized_ = false;
        decoder1_ = NULL;
        decoder2_ = NULL;
        decoder3_ = NULL;
        video_frame_stream_.reset();
        base::RunLoop().RunUntilIdle();

        DCHECK(!pending_initialize_);
        DCHECK(!pending_read_);
        DCHECK(!pending_reset_);
        DCHECK(!pending_stop_);
    }

    MOCK_METHOD0(OnWaitingForDecryptionKey, void(void));

    void OnStatistics(const PipelineStatistics& statistics)
    {
        num_decoded_bytes_unreported_ -= statistics.video_bytes_decoded;
    }

    void OnBytesDecoded(int count)
    {
        num_decoded_bytes_unreported_ += count;
    }

    void OnInitialized(bool success)
    {
        DCHECK(!pending_read_);
        DCHECK(!pending_reset_);
        DCHECK(pending_initialize_);
        pending_initialize_ = false;

        is_initialized_ = success;
        if (!success) {
            decoder1_ = NULL;
            decoder2_ = NULL;
            decoder3_ = NULL;
        }
    }

    void InitializeVideoFrameStream()
    {
        pending_initialize_ = true;
        video_frame_stream_->Initialize(
            demuxer_stream_.get(), base::Bind(&VideoFrameStreamTest::OnInitialized, base::Unretained(this)),
            cdm_context_.get(),
            base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)),
            base::Bind(&VideoFrameStreamTest::OnWaitingForDecryptionKey,
                base::Unretained(this)));
        base::RunLoop().RunUntilIdle();
    }

    // Fake Decrypt() function used by DecryptingDemuxerStream. It does nothing
    // but removes the DecryptConfig to make the buffer unencrypted.
    void Decrypt(Decryptor::StreamType stream_type,
        const scoped_refptr<DecoderBuffer>& encrypted,
        const Decryptor::DecryptCB& decrypt_cb)
    {
        DCHECK(encrypted->decrypt_config());
        if (has_no_key_) {
            decrypt_cb.Run(Decryptor::kNoKey, NULL);
            return;
        }

        DCHECK_EQ(stream_type, Decryptor::kVideo);
        scoped_refptr<DecoderBuffer> decrypted = DecoderBuffer::CopyFrom(encrypted->data(), encrypted->data_size());
        if (encrypted->is_key_frame())
            decrypted->set_is_key_frame(true);
        decrypted->set_timestamp(encrypted->timestamp());
        decrypted->set_duration(encrypted->duration());
        decrypt_cb.Run(Decryptor::kSuccess, decrypted);
    }

    // Callback for VideoFrameStream::Read().
    void FrameReady(VideoFrameStream::Status status,
        const scoped_refptr<VideoFrame>& frame)
    {
        DCHECK(pending_read_);
        frame_read_ = frame;
        last_read_status_ = status;
        if (frame.get() && !frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
            num_decoded_frames_++;
        }
        pending_read_ = false;
    }

    void OnReset()
    {
        DCHECK(!pending_read_);
        DCHECK(pending_reset_);
        pending_reset_ = false;
    }

    void ReadOneFrame()
    {
        frame_read_ = NULL;
        pending_read_ = true;
        video_frame_stream_->Read(base::Bind(
            &VideoFrameStreamTest::FrameReady, base::Unretained(this)));
        base::RunLoop().RunUntilIdle();
    }

    void ReadUntilPending()
    {
        do {
            ReadOneFrame();
        } while (!pending_read_);
    }

    void ReadAllFrames(int expected_decoded_frames)
    {
        do {
            ReadOneFrame();
        } while (frame_read_.get() && !frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));

        DCHECK_EQ(expected_decoded_frames, num_decoded_frames_);
    }

    void ReadAllFrames()
    {
        // No frames should have been dropped.
        ReadAllFrames(kNumConfigs * kNumBuffersInOneConfig);
    }

    enum PendingState {
        NOT_PENDING,
        DEMUXER_READ_NORMAL,
        DEMUXER_READ_CONFIG_CHANGE,
        DECRYPTOR_NO_KEY,
        DECODER_INIT,
        DECODER_REINIT,
        DECODER_DECODE,
        DECODER_RESET
    };

    void EnterPendingState(PendingState state)
    {
        EnterPendingState(state, decoder1_);
    }

    void EnterPendingState(PendingState state, FakeVideoDecoder* decoder)
    {
        DCHECK_NE(state, NOT_PENDING);
        switch (state) {
        case DEMUXER_READ_NORMAL:
            demuxer_stream_->HoldNextRead();
            ReadUntilPending();
            break;

        case DEMUXER_READ_CONFIG_CHANGE:
            demuxer_stream_->HoldNextConfigChangeRead();
            ReadUntilPending();
            break;

        case DECRYPTOR_NO_KEY:
            if (GetParam().is_encrypted && GetParam().has_decryptor) {
                EXPECT_CALL(*this, OnWaitingForDecryptionKey());
                has_no_key_ = true;
            }
            ReadOneFrame();
            break;

        case DECODER_INIT:
            decoder->HoldNextInit();
            InitializeVideoFrameStream();
            break;

        case DECODER_REINIT:
            decoder->HoldNextInit();
            ReadUntilPending();
            break;

        case DECODER_DECODE:
            decoder->HoldDecode();
            ReadUntilPending();
            break;

        case DECODER_RESET:
            decoder->HoldNextReset();
            pending_reset_ = true;
            video_frame_stream_->Reset(base::Bind(&VideoFrameStreamTest::OnReset,
                base::Unretained(this)));
            base::RunLoop().RunUntilIdle();
            break;

        case NOT_PENDING:
            NOTREACHED();
            break;
        }
    }

    void SatisfyPendingCallback(PendingState state)
    {
        SatisfyPendingCallback(state, decoder1_);
    }

    void SatisfyPendingCallback(PendingState state, FakeVideoDecoder* decoder)
    {
        DCHECK_NE(state, NOT_PENDING);
        switch (state) {
        case DEMUXER_READ_NORMAL:
        case DEMUXER_READ_CONFIG_CHANGE:
            demuxer_stream_->SatisfyRead();
            break;

        // This is only interesting to test during VideoFrameStream destruction.
        // There's no need to satisfy a callback.
        case DECRYPTOR_NO_KEY:
            NOTREACHED();
            break;

        case DECODER_INIT:
            decoder->SatisfyInit();
            break;

        case DECODER_REINIT:
            decoder->SatisfyInit();
            break;

        case DECODER_DECODE:
            decoder->SatisfyDecode();
            break;

        case DECODER_RESET:
            decoder->SatisfyReset();
            break;

        case NOT_PENDING:
            NOTREACHED();
            break;
        }

        base::RunLoop().RunUntilIdle();
    }

    void Initialize()
    {
        EnterPendingState(DECODER_INIT);
        SatisfyPendingCallback(DECODER_INIT);
    }

    void Read()
    {
        EnterPendingState(DECODER_DECODE);
        SatisfyPendingCallback(DECODER_DECODE);
    }

    void Reset()
    {
        EnterPendingState(DECODER_RESET);
        SatisfyPendingCallback(DECODER_RESET);
    }

    void ReadUntilDecoderReinitialized(FakeVideoDecoder* decoder)
    {
        EnterPendingState(DECODER_REINIT, decoder);
        SatisfyPendingCallback(DECODER_REINIT, decoder);
    }

    base::MessageLoop message_loop_;

    std::unique_ptr<VideoFrameStream> video_frame_stream_;
    std::unique_ptr<FakeDemuxerStream> demuxer_stream_;
    std::unique_ptr<StrictMock<MockCdmContext>> cdm_context_;

    // Use NiceMock since we don't care about most of calls on the decryptor,
    // e.g. RegisterNewKeyCB().
    std::unique_ptr<NiceMock<MockDecryptor>> decryptor_;

    // Three decoders are needed to test that decoder fallback can occur more than
    // once on a config change. They are owned by |video_frame_stream_|.
    FakeVideoDecoder* decoder1_;
    FakeVideoDecoder* decoder2_;
    FakeVideoDecoder* decoder3_;

    bool is_initialized_;
    int num_decoded_frames_;
    bool pending_initialize_;
    bool pending_read_;
    bool pending_reset_;
    bool pending_stop_;
    int num_decoded_bytes_unreported_;
    scoped_refptr<VideoFrame> frame_read_;
    VideoFrameStream::Status last_read_status_;

    // Decryptor has no key to decrypt a frame.
    bool has_no_key_;

private:
    DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest);
};

INSTANTIATE_TEST_CASE_P(
    Clear,
    VideoFrameStreamTest,
    ::testing::Values(VideoFrameStreamTestParams(false, false, 0, 1),
        VideoFrameStreamTestParams(false, false, 3, 1),
        VideoFrameStreamTestParams(false, false, 7, 1)));

INSTANTIATE_TEST_CASE_P(
    EncryptedWithDecryptor,
    VideoFrameStreamTest,
    ::testing::Values(VideoFrameStreamTestParams(true, true, 7, 1)));

INSTANTIATE_TEST_CASE_P(
    EncryptedWithoutDecryptor,
    VideoFrameStreamTest,
    ::testing::Values(VideoFrameStreamTestParams(true, false, 7, 1)));

INSTANTIATE_TEST_CASE_P(
    Clear_Parallel,
    VideoFrameStreamTest,
    ::testing::Values(VideoFrameStreamTestParams(false, false, 0, 3),
        VideoFrameStreamTestParams(false, false, 2, 3)));

TEST_P(VideoFrameStreamTest, Initialization)
{
    Initialize();
    EXPECT_TRUE(is_initialized_);
}

TEST_P(VideoFrameStreamTest, AllDecoderInitializationFails)
{
    decoder1_->SimulateFailureToInit();
    decoder2_->SimulateFailureToInit();
    decoder3_->SimulateFailureToInit();
    Initialize();
    EXPECT_FALSE(is_initialized_);
}

TEST_P(VideoFrameStreamTest, PartialDecoderInitializationFails)
{
    decoder1_->SimulateFailureToInit();
    decoder2_->SimulateFailureToInit();
    Initialize();
    EXPECT_TRUE(is_initialized_);
}

TEST_P(VideoFrameStreamTest, ReadOneFrame)
{
    Initialize();
    Read();
}

TEST_P(VideoFrameStreamTest, ReadAllFrames)
{
    Initialize();
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, Read_AfterReset)
{
    Initialize();
    Reset();
    Read();
    Reset();
    Read();
}

TEST_P(VideoFrameStreamTest, Read_BlockedDemuxer)
{
    Initialize();
    demuxer_stream_->HoldNextRead();
    ReadOneFrame();
    EXPECT_TRUE(pending_read_);

    int demuxed_buffers = 0;

    // Pass frames from the demuxer to the VideoFrameStream until the first read
    // request is satisfied.
    while (pending_read_) {
        ++demuxed_buffers;
        demuxer_stream_->SatisfyReadAndHoldNext();
        base::RunLoop().RunUntilIdle();
    }

    EXPECT_EQ(std::min(GetParam().decoding_delay + 1, kNumBuffersInOneConfig + 1),
        demuxed_buffers);

    // At this point the stream is waiting on read from the demuxer, but there is
    // no pending read from the stream. The stream should be blocked if we try
    // reading from it again.
    ReadUntilPending();

    demuxer_stream_->SatisfyRead();
    base::RunLoop().RunUntilIdle();
    EXPECT_FALSE(pending_read_);
}

TEST_P(VideoFrameStreamTest, Read_BlockedDemuxerAndDecoder)
{
    // Test applies only when the decoder allows multiple parallel requests.
    if (GetParam().parallel_decoding == 1)
        return;

    Initialize();
    demuxer_stream_->HoldNextRead();
    decoder1_->HoldDecode();
    ReadOneFrame();
    EXPECT_TRUE(pending_read_);

    int demuxed_buffers = 0;

    // Pass frames from the demuxer to the VideoFrameStream until the first read
    // request is satisfied, while always keeping one decode request pending.
    while (pending_read_) {
        ++demuxed_buffers;
        demuxer_stream_->SatisfyReadAndHoldNext();
        base::RunLoop().RunUntilIdle();

        // Always keep one decode request pending.
        if (demuxed_buffers > 1) {
            decoder1_->SatisfySingleDecode();
            base::RunLoop().RunUntilIdle();
        }
    }

    ReadUntilPending();
    EXPECT_TRUE(pending_read_);

    // Unblocking one decode request should unblock read even when demuxer is
    // still blocked.
    decoder1_->SatisfySingleDecode();
    base::RunLoop().RunUntilIdle();
    EXPECT_FALSE(pending_read_);

    // Stream should still be blocked on the demuxer after unblocking the decoder.
    decoder1_->SatisfyDecode();
    ReadUntilPending();
    EXPECT_TRUE(pending_read_);

    // Verify that the stream has returned all frames that have been demuxed,
    // accounting for the decoder delay.
    EXPECT_EQ(demuxed_buffers - GetParam().decoding_delay, num_decoded_frames_);

    // Unblocking the demuxer will unblock the stream.
    demuxer_stream_->SatisfyRead();
    base::RunLoop().RunUntilIdle();
    EXPECT_FALSE(pending_read_);
}

TEST_P(VideoFrameStreamTest, Read_DuringEndOfStreamDecode)
{
    // Test applies only when the decoder allows multiple parallel requests, and
    // they are not satisfied in a single batch.
    if (GetParam().parallel_decoding == 1 || GetParam().decoding_delay != 0)
        return;

    Initialize();
    decoder1_->HoldDecode();

    // Read all of the frames up to end of stream. Since parallel decoding is
    // enabled, the end of stream buffer will be sent to the decoder immediately,
    // but we don't satisfy it yet.
    for (int configuration = 0; configuration < kNumConfigs; configuration++) {
        for (int frame = 0; frame < kNumBuffersInOneConfig; frame++) {
            ReadOneFrame();
            while (pending_read_) {
                decoder1_->SatisfySingleDecode();
                base::RunLoop().RunUntilIdle();
            }
        }
    }

    // Read() again. The callback must be delayed until the decode completes.
    ReadOneFrame();
    ASSERT_TRUE(pending_read_);

    // Satisfy decoding of the end of stream buffer. The read should complete.
    decoder1_->SatisfySingleDecode();
    base::RunLoop().RunUntilIdle();
    ASSERT_FALSE(pending_read_);
    EXPECT_EQ(last_read_status_, VideoFrameStream::OK);

    // The read output should indicate end of stream.
    ASSERT_TRUE(frame_read_.get());
    EXPECT_TRUE(
        frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
}

// No Reset() before initialization is successfully completed.
TEST_P(VideoFrameStreamTest, Reset_AfterInitialization)
{
    Initialize();
    Reset();
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_DuringReinitialization)
{
    Initialize();
    EnterPendingState(DECODER_REINIT);
    // VideoDecoder::Reset() is not called when we reset during reinitialization.
    pending_reset_ = true;
    video_frame_stream_->Reset(
        base::Bind(&VideoFrameStreamTest::OnReset, base::Unretained(this)));
    SatisfyPendingCallback(DECODER_REINIT);
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_AfterReinitialization)
{
    Initialize();
    EnterPendingState(DECODER_REINIT);
    SatisfyPendingCallback(DECODER_REINIT);
    Reset();
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_Normal)
{
    Initialize();
    EnterPendingState(DEMUXER_READ_NORMAL);
    EnterPendingState(DECODER_RESET);
    SatisfyPendingCallback(DEMUXER_READ_NORMAL);
    SatisfyPendingCallback(DECODER_RESET);
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_ConfigChange)
{
    Initialize();
    EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
    EnterPendingState(DECODER_RESET);
    SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
    SatisfyPendingCallback(DECODER_RESET);
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_DuringNormalDecoderDecode)
{
    Initialize();
    EnterPendingState(DECODER_DECODE);
    EnterPendingState(DECODER_RESET);
    SatisfyPendingCallback(DECODER_DECODE);
    SatisfyPendingCallback(DECODER_RESET);
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_AfterNormalRead)
{
    Initialize();
    Read();
    Reset();
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_AfterDemuxerRead_ConfigChange)
{
    Initialize();
    EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
    SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
    Reset();
    Read();
}

TEST_P(VideoFrameStreamTest, Reset_AfterEndOfStream)
{
    Initialize();
    ReadAllFrames();
    Reset();
    num_decoded_frames_ = 0;
    demuxer_stream_->SeekToStart();
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, Reset_DuringNoKeyRead)
{
    Initialize();
    EnterPendingState(DECRYPTOR_NO_KEY);
    Reset();
}

// In the following Destroy_* tests, |video_frame_stream_| is destroyed in
// VideoFrameStreamTest dtor.

TEST_P(VideoFrameStreamTest, Destroy_BeforeInitialization)
{
}

TEST_P(VideoFrameStreamTest, Destroy_DuringInitialization)
{
    EnterPendingState(DECODER_INIT);
}

TEST_P(VideoFrameStreamTest, Destroy_AfterInitialization)
{
    Initialize();
}

TEST_P(VideoFrameStreamTest, Destroy_DuringReinitialization)
{
    Initialize();
    EnterPendingState(DECODER_REINIT);
}

TEST_P(VideoFrameStreamTest, Destroy_AfterReinitialization)
{
    Initialize();
    EnterPendingState(DECODER_REINIT);
    SatisfyPendingCallback(DECODER_REINIT);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringDemuxerRead_Normal)
{
    Initialize();
    EnterPendingState(DEMUXER_READ_NORMAL);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringDemuxerRead_ConfigChange)
{
    Initialize();
    EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringNormalDecoderDecode)
{
    Initialize();
    EnterPendingState(DECODER_DECODE);
}

TEST_P(VideoFrameStreamTest, Destroy_AfterNormalRead)
{
    Initialize();
    Read();
}

TEST_P(VideoFrameStreamTest, Destroy_AfterConfigChangeRead)
{
    Initialize();
    EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
    SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringDecoderReinitialization)
{
    Initialize();
    EnterPendingState(DECODER_REINIT);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringNoKeyRead)
{
    Initialize();
    EnterPendingState(DECRYPTOR_NO_KEY);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringReset)
{
    Initialize();
    EnterPendingState(DECODER_RESET);
}

TEST_P(VideoFrameStreamTest, Destroy_AfterReset)
{
    Initialize();
    Reset();
}

TEST_P(VideoFrameStreamTest, Destroy_DuringRead_DuringReset)
{
    Initialize();
    EnterPendingState(DECODER_DECODE);
    EnterPendingState(DECODER_RESET);
}

TEST_P(VideoFrameStreamTest, Destroy_AfterRead_DuringReset)
{
    Initialize();
    EnterPendingState(DECODER_DECODE);
    EnterPendingState(DECODER_RESET);
    SatisfyPendingCallback(DECODER_DECODE);
}

TEST_P(VideoFrameStreamTest, Destroy_AfterRead_AfterReset)
{
    Initialize();
    Read();
    Reset();
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitialDecodeError)
{
    Initialize();
    decoder1_->SimulateError();
    ReadOneFrame();

    // |video_frame_stream_| should have fallen back to |decoder2_|.
    ASSERT_FALSE(pending_read_);
    ASSERT_EQ(VideoFrameStream::OK, last_read_status_);

    // Can't check |decoder1_| right now, it might have been destroyed already.
    ASSERT_GT(decoder2_->total_bytes_decoded(), 0);

    // Verify no frame was dropped.
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_EndOfStreamReachedBeforeFallback)
{
    // Only consider cases where there is a decoder delay. For test simplicity,
    // omit the parallel case.
    if (GetParam().decoding_delay == 0 || GetParam().parallel_decoding > 1)
        return;

    Initialize();
    decoder1_->HoldDecode();
    ReadOneFrame();

    // One buffer should have already pulled from the demuxer stream. Set the next
    // one to be an EOS.
    demuxer_stream_->SeekToEndOfStream();

    decoder1_->SatisfySingleDecode();
    base::RunLoop().RunUntilIdle();

    // |video_frame_stream_| should not have emited a frame.
    EXPECT_TRUE(pending_read_);

    // Pending buffers should contain a regular buffer and an EOS buffer.
    EXPECT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 2);

    decoder1_->SimulateError();
    base::RunLoop().RunUntilIdle();

    //  A frame should have been emited
    EXPECT_FALSE(pending_read_);
    EXPECT_EQ(last_read_status_, VideoFrameStream::OK);
    EXPECT_FALSE(
        frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
    EXPECT_GT(decoder2_->total_bytes_decoded(), 0);

    ReadOneFrame();

    EXPECT_FALSE(pending_read_);
    EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing());
    EXPECT_TRUE(
        frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_DoesReinitializeStompPendingRead)
{
    // Test only the case where there is no decoding delay and parallel decoding.
    if (GetParam().decoding_delay != 0 || GetParam().parallel_decoding <= 1)
        return;

    Initialize();
    decoder1_->HoldDecode();

    // Queue one read, defer the second.
    frame_read_ = nullptr;
    pending_read_ = true;
    video_frame_stream_->Read(
        base::Bind(&VideoFrameStreamTest::FrameReady, base::Unretained(this)));
    demuxer_stream_->HoldNextRead();

    // Force an error to occur on the first decode, but ensure it isn't propagated
    // until after the next read has been started.
    decoder1_->SimulateError();
    decoder2_->HoldDecode();

    // Complete the fallback to the second decoder with the read still pending.
    base::RunLoop().RunUntilIdle();

    // Can't check |decoder1_| right now, it might have been destroyed already.
    // Verify that there was nothing decoded until we kicked the decoder.
    EXPECT_EQ(decoder2_->total_bytes_decoded(), 0);
    decoder2_->SatisfyDecode();
    const int first_decoded_bytes = decoder2_->total_bytes_decoded();
    ASSERT_GT(first_decoded_bytes, 0);

    // Satisfy the previously pending read and ensure it is decoded.
    demuxer_stream_->SatisfyRead();
    base::RunLoop().RunUntilIdle();
    ASSERT_GT(decoder2_->total_bytes_decoded(), first_decoded_bytes);
}

TEST_P(VideoFrameStreamTest,
    FallbackDecoder_SelectedOnInitialDecodeError_Twice)
{
    Initialize();
    decoder1_->SimulateError();
    decoder2_->HoldNextInit();
    ReadOneFrame();

    decoder2_->SatisfyInit();
    decoder2_->SimulateError();
    base::RunLoop().RunUntilIdle();

    // |video_frame_stream_| should have fallen back to |decoder3_|.
    ASSERT_FALSE(pending_read_);
    ASSERT_EQ(VideoFrameStream::OK, last_read_status_);

    // Can't check |decoder1_| or |decoder2_| right now, they might have been
    // destroyed already.
    ASSERT_GT(decoder3_->total_bytes_decoded(), 0);

    // Verify no frame was dropped.
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_ConfigChangeClearsPendingBuffers)
{
    // Test case is only interesting if the decoder can receive a config change
    // before returning its first frame.
    if (GetParam().decoding_delay < kNumBuffersInOneConfig)
        return;

    Initialize();
    EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
    ASSERT_GT(video_frame_stream_->get_pending_buffers_size_for_testing(), 0);

    SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
    ASSERT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 0);
    EXPECT_FALSE(pending_read_);

    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_ErrorDuringConfigChangeFlushing)
{
    // Test case is only interesting if the decoder can receive a config change
    // before returning its first frame.
    if (GetParam().decoding_delay < kNumBuffersInOneConfig)
        return;

    Initialize();
    EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
    EXPECT_GT(video_frame_stream_->get_pending_buffers_size_for_testing(), 0);

    decoder1_->HoldDecode();
    SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);

    // The flush request should have been sent and held.
    EXPECT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 0);
    EXPECT_TRUE(pending_read_);

    // Triggering an error here will cause the frames in |decoder1_| to be lost.
    // There are no pending buffers buffers to give to give to |decoder2_| due to
    // crbug.com/603713.
    decoder1_->SimulateError();
    base::RunLoop().RunUntilIdle();

    // We want to make sure that |decoder2_| can decode the rest of the frames
    // in the demuxer stream.
    ReadAllFrames(kNumBuffersInOneConfig * (kNumConfigs - 1));
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared)
{
    // Test applies only when there is a decoder delay, and the decoder will not
    // receive a config change before outputing its first frame. Parallel decoding
    // is also disabled in this test case, for readability and simplicity of the
    // unit test.
    if (GetParam().decoding_delay == 0 || GetParam().decoding_delay > kNumBuffersInOneConfig || GetParam().parallel_decoding > 1) {
        return;
    }
    Initialize();

    // Block on demuxer read and decoder decode so we can step through.
    demuxer_stream_->HoldNextRead();
    decoder1_->HoldDecode();
    ReadOneFrame();

    int demuxer_reads_satisfied = 0;
    // Send back and requests buffers until the next one would fill the decoder
    // delay.
    while (demuxer_reads_satisfied < GetParam().decoding_delay - 1) {
        // Send a buffer back.
        demuxer_stream_->SatisfyReadAndHoldNext();
        base::RunLoop().RunUntilIdle();
        ++demuxer_reads_satisfied;

        // Decode one buffer.
        decoder1_->SatisfySingleDecode();
        base::RunLoop().RunUntilIdle();
        EXPECT_TRUE(pending_read_);
        EXPECT_EQ(demuxer_reads_satisfied,
            video_frame_stream_->get_pending_buffers_size_for_testing());
        // No fallback buffers should be queued up yet.
        EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing());
    }

    // Hold the init before triggering the error, to verify internal state.
    demuxer_stream_->SatisfyReadAndHoldNext();
    ++demuxer_reads_satisfied;
    decoder2_->HoldNextInit();
    decoder1_->SimulateError();
    base::RunLoop().RunUntilIdle();

    EXPECT_TRUE(pending_read_);
    EXPECT_EQ(demuxer_reads_satisfied,
        video_frame_stream_->get_pending_buffers_size_for_testing());

    decoder2_->SatisfyInit();
    decoder2_->HoldDecode();
    base::RunLoop().RunUntilIdle();

    // Make sure the pending buffers have been transfered to fallback buffers.
    // One call to Decode() during the initialization process, so we expect one
    // buffer to already have been consumed from the fallback buffers.
    // Pending buffers should never go down (unless we encounter a config change)
    EXPECT_EQ(demuxer_reads_satisfied - 1,
        video_frame_stream_->get_fallback_buffers_size_for_testing());
    EXPECT_EQ(demuxer_reads_satisfied,
        video_frame_stream_->get_pending_buffers_size_for_testing());

    decoder2_->SatisfyDecode();
    base::RunLoop().RunUntilIdle();

    // Make sure all buffers consumed by |decoder2_| have come from the fallback.
    // Pending buffers should not have been cleared yet.
    EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing());
    EXPECT_EQ(demuxer_reads_satisfied,
        video_frame_stream_->get_pending_buffers_size_for_testing());
    EXPECT_EQ(video_frame_stream_->get_previous_decoder_for_testing(), decoder1_);
    EXPECT_TRUE(pending_read_);

    // Give the decoder one more buffer, enough to release a frame.
    demuxer_stream_->SatisfyReadAndHoldNext();
    base::RunLoop().RunUntilIdle();

    // New buffers should not have been added after the frame was released.
    EXPECT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 0);
    EXPECT_FALSE(pending_read_);

    demuxer_stream_->SatisfyRead();

    // Confirm no frames were dropped.
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnDecodeThenInitErrors)
{
    Initialize();
    decoder1_->SimulateError();
    decoder2_->SimulateFailureToInit();
    ReadOneFrame();

    // |video_frame_stream_| should have fallen back to |decoder3_|
    ASSERT_FALSE(pending_read_);
    ASSERT_EQ(VideoFrameStream::OK, last_read_status_);

    // Can't check |decoder1_| or |decoder2_| right now, they might have been
    // destroyed already.
    ASSERT_GT(decoder3_->total_bytes_decoded(), 0);

    // Verify no frame was dropped.
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitThenDecodeErrors)
{
    decoder1_->SimulateFailureToInit();
    decoder2_->HoldDecode();
    Initialize();
    ReadOneFrame();
    decoder2_->SimulateError();
    base::RunLoop().RunUntilIdle();

    // |video_frame_stream_| should have fallen back to |decoder3_|
    ASSERT_FALSE(pending_read_);
    ASSERT_EQ(VideoFrameStream::OK, last_read_status_);

    // Can't check |decoder1_| or |decoder2_| right now, they might have been
    // destroyed already.
    ASSERT_GT(decoder3_->total_bytes_decoded(), 0);

    // Verify no frame was dropped.
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest,
    FallbackDecoder_NotSelectedOnMidstreamDecodeError)
{
    Initialize();
    ReadOneFrame();

    // Successfully received a frame.
    EXPECT_FALSE(pending_read_);
    ASSERT_GT(decoder1_->total_bytes_decoded(), 0);

    decoder1_->SimulateError();

    // The error must surface from Read() as DECODE_ERROR.
    while (last_read_status_ == VideoFrameStream::OK) {
        ReadOneFrame();
        base::RunLoop().RunUntilIdle();
        EXPECT_FALSE(pending_read_);
    }

    // Verify the error was surfaced, rather than falling back to |decoder2_|.
    EXPECT_FALSE(pending_read_);
    ASSERT_EQ(decoder2_->total_bytes_decoded(), 0);
    ASSERT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
}

TEST_P(VideoFrameStreamTest, DecoderErrorWhenNotReading)
{
    Initialize();
    decoder1_->HoldDecode();
    ReadOneFrame();
    EXPECT_TRUE(pending_read_);

    // Satisfy decode requests until we get the first frame out.
    while (pending_read_) {
        decoder1_->SatisfySingleDecode();
        base::RunLoop().RunUntilIdle();
    }

    // Trigger an error in the decoding.
    decoder1_->SimulateError();

    // The error must surface from Read() as DECODE_ERROR.
    while (last_read_status_ == VideoFrameStream::OK) {
        ReadOneFrame();
        base::RunLoop().RunUntilIdle();
        EXPECT_FALSE(pending_read_);
    }
    EXPECT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
}

TEST_P(VideoFrameStreamTest, FallbackDecoderSelectedOnFailureToReinitialize)
{
    Initialize();
    decoder1_->SimulateFailureToInit();
    // Holding decode, because large decoder delays might cause us to get rid of
    // |previous_decoder_| before we are in a pending state again.
    decoder2_->HoldDecode();
    ReadUntilDecoderReinitialized(decoder1_);
    ASSERT_TRUE(video_frame_stream_->get_previous_decoder_for_testing());
    decoder2_->SatisfyDecode();
    base::RunLoop().RunUntilIdle();
    ReadAllFrames();
    ASSERT_FALSE(video_frame_stream_->get_previous_decoder_for_testing());
}

TEST_P(VideoFrameStreamTest,
    FallbackDecoderSelectedOnFailureToReinitialize_Twice)
{
    Initialize();
    decoder1_->SimulateFailureToInit();
    ReadUntilDecoderReinitialized(decoder1_);
    ReadOneFrame();
    decoder2_->SimulateFailureToInit();
    ReadUntilDecoderReinitialized(decoder2_);
    ReadAllFrames();
}

TEST_P(VideoFrameStreamTest, DecodeErrorAfterFallbackDecoderSelectionFails)
{
    Initialize();
    decoder1_->SimulateFailureToInit();
    decoder2_->SimulateFailureToInit();
    decoder3_->SimulateFailureToInit();
    ReadUntilDecoderReinitialized(decoder1_);
    // The error will surface from Read() as DECODE_ERROR.
    while (last_read_status_ == VideoFrameStream::OK) {
        ReadOneFrame();
        base::RunLoop().RunUntilIdle();
        EXPECT_FALSE(pending_read_);
    }
    EXPECT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
}

TEST_P(VideoFrameStreamTest, Destroy_DuringFallbackDecoderSelection)
{
    Initialize();
    decoder1_->SimulateFailureToInit();
    EnterPendingState(DECODER_REINIT);
    decoder2_->HoldNextInit();
    SatisfyPendingCallback(DECODER_REINIT);
}

} // namespace media
